﻿using CommonNet48.LogUtils.Log4net;
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Threading.Tasks;

namespace CommonNet48.Net
{
    public class HttpFileDownLoad
    {
        /// <summary>
        /// 日志操作对象
        /// </summary>
        private readonly Log4 _Logger = new Log4(typeof(HttpFileDownLoad));

        /// <summary>
        /// 请求超时时间，单位毫秒
        /// </summary>
        public int Timeout { get; set; } = 6000;

        /// <summary>
        /// 流读写超时时间，单位毫秒
        /// </summary>
        public int ReadWriteTimeout { get; set; } = 2000;

        /// <summary>
        /// 进度处理委托。参数1为当前接收大小，参数2为下载文件总大小,参数3为用时(毫秒),参数4为速度(kb/s)
        /// </summary>
        public Action<long, long, long, decimal> Act_Progress = null;

        /// <summary>
        /// 文件下载地址
        /// </summary>
        private string Url { get; set; } = string.Empty;

        /// <summary>
        /// 本地保存文件位置(完整文件路径)
        /// </summary>
        private string FileSaveFullPath { get; set; } = string.Empty;


        /// <summary>
        /// 构造
        /// </summary>
        /// <param name="fileUrl">文件下载地址</param>
        /// <param name="savePath">本地保存文件位置(完整文件路径)</param>
        public HttpFileDownLoad(string fileUrl, string savePath)
        {
            Url = fileUrl;
            FileSaveFullPath = savePath;
        }

        /// <summary>
        /// Http文件下载
        /// </summary>
        public async void DownLoadFile()
        {
            await Task.Run(() =>
            {
                FileStream fs = null;
                HttpWebRequest request = null;
                try
                {
                    string dirName = Path.GetDirectoryName(FileSaveFullPath);
                    string fileName = Path.GetFileName(FileSaveFullPath);
                    string path = Path.Combine(dirName, $"{fileName}.temp");
                    long startpos;
                    if (File.Exists(path))
                    {
                        fs = File.OpenWrite(path);
                        startpos = fs.Length;
                        fs.Seek(startpos, SeekOrigin.Current);
                    }
                    else
                    {
                        if (!Directory.Exists(dirName))
                        {
                            Directory.CreateDirectory(dirName);
                        }
                        fs = new FileStream(path, FileMode.Create);
                        startpos = 0;
                    }
                    request = (HttpWebRequest)HttpWebRequest.Create(Url);
                    request.Timeout = Timeout;
                    request.ReadWriteTimeout = ReadWriteTimeout;
                    if (startpos > 0) request.AddRange((int)startpos);
                    WebResponse response = request.GetResponse();
                    long totalsize = response.ContentLength + startpos;
                    long currsize = startpos;
                    if (currsize > totalsize) throw new Exception("下载文件大小匹配异常，请重新下载！");
                    Stopwatch watch = Stopwatch.StartNew();
                    using (Stream stream = response.GetResponseStream())
                    {
                        byte[] buffer = new byte[4096];
                        int readsize = stream.Read(buffer, 0, buffer.Length);
                        int recCount = readsize;//接收数据长度
                        long saveseconds = watch.ElapsedMilliseconds;//记录本次速度计算起始时间
                        decimal speed = 0;//速度
                        Act_Progress?.Invoke(currsize, totalsize, watch.ElapsedMilliseconds, speed);
                        while (readsize > 0)
                        {
                            fs.Write(buffer, 0, readsize);
                            currsize += readsize;
                            if (currsize > totalsize) throw new Exception("下载文件大小匹配异常，请重新下载！");
                            long speedcalctime = watch.ElapsedMilliseconds - saveseconds;
                            if (speedcalctime > 1000)
                            {
                                speed = (recCount / 1024m) / (speedcalctime / 1000m);
                                saveseconds = watch.ElapsedMilliseconds;
                                recCount = 0;
                            }
                            Act_Progress?.Invoke(currsize, totalsize, watch.ElapsedMilliseconds, speed);
                            readsize = stream.Read(buffer, 0, buffer.Length);
                            recCount += readsize;
                        }
                        watch.Stop();
                        fs.Close();
                        stream.Close();
                        File.Move(path, FileSaveFullPath);
                    }
                }
                catch (Exception ex)
                {
                    fs?.Close();
                    _Logger.Error($"下载文件发生异常！\r\n{ex.Message}", ex);
                }
                finally
                {
                    request?.Abort();

                }
            });
        }
    }
}
